package ink.ykb.demo.permission.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import ink.ykb.demo.config.CodeConstant;
import ink.ykb.demo.config.MessageUtil;
import ink.ykb.demo.config.ServiceException;
import ink.ykb.demo.permission.dao.RuleDao;
import ink.ykb.demo.permission.entity.DealerDepartment;
import ink.ykb.demo.permission.entity.RoleRule;
import ink.ykb.demo.permission.entity.Rule;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class RuleServiceImpl implements RuleService {

	@Autowired
	private MessageUtil messageUtil;
	@Autowired
	private RuleDao ruleDao;
	@Autowired
	private RoleRuleService roleRuleService;
	@Autowired
	private RuleService self;
	@Autowired
	private ObjectMapper objectMapper;

	
	@Caching(evict = { 
			@CacheEvict(cacheNames = { "ruleListByType" }, key = "#rule.type"),
			@CacheEvict(cacheNames = { "ruleList" }, allEntries = true),
			@CacheEvict(cacheNames = { "ruleListByParentId" }, allEntries = true)
	})
	@Transactional
	@Override
	public void add(Rule rule) {
		if (rule == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getName() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (StringUtils.isBlank(rule.getCode())) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getServiceName() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getUri() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getMethod() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getType() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getParentId() == null) {
			rule.setParentId(0L);
		}

		Rule dbRule = ruleDao.getRuleAndLock(rule.getCode());
		if (dbRule != null) {
			throw new ServiceException(CodeConstant.MUS_100, messageUtil.getMsg(CodeConstant.MUS_100));
		}

		rule.setCreateTime(new Date());
		ruleDao.insert(rule);

	}

	@Caching(evict = { @CacheEvict(cacheNames = { "rule" }, key = "#rule.id"),
			@CacheEvict(cacheNames = { "ruleListByType" }, key = "#rule.type"),
			@CacheEvict(cacheNames = { "ruleList" }, allEntries = true),
			@CacheEvict(cacheNames = { "ruleListByParentId" }, allEntries = true)
	})
	@Transactional
	@Override
	public void update(Rule rule) {
		if (rule == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getId() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getName() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (StringUtils.isBlank(rule.getCode())) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getServiceName() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getUri() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getMethod() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getType() == null) {
			throw new ServiceException(CodeConstant.PARAMS_EMPTY, messageUtil.getMsg(CodeConstant.PARAMS_EMPTY));
		}
		if (rule.getParentId() == null) {
			rule.setParentId(0L);
		}

		// 判断code是否重复
		Rule dbCodeRule = ruleDao.getRuleAndLock(rule.getCode());
		if (dbCodeRule != null && !dbCodeRule.getId().equals(rule.getId())) {
			throw new ServiceException(CodeConstant.MUS_100, messageUtil.getMsg(CodeConstant.MUS_100));
		}

		Rule dbRule = ruleDao.getRuleById(rule.getId());
		if (dbRule == null) {
			throw new ServiceException(CodeConstant.MUS_101, messageUtil.getMsg(CodeConstant.MUS_101));
		}

		dbRule.setName(rule.getName());
		dbRule.setCode(rule.getCode());
		dbRule.setServiceName(rule.getServiceName());
		dbRule.setUri(rule.getUri());
		dbRule.setMethod(rule.getMethod());
		dbRule.setType(rule.getType());
		dbRule.setParentId(rule.getParentId());
		ruleDao.updateById(dbRule);
	}

	@Caching(evict = { 
			@CacheEvict(cacheNames = { "rule" }, allEntries = true),
			@CacheEvict(cacheNames = { "ruleList" }, allEntries = true),
			@CacheEvict(cacheNames = { "ruleListByType" }, allEntries = true),
			@CacheEvict(cacheNames = { "ruleListByParentId" }, allEntries = true)
	})
	@Transactional
	@Override
	public void delete(Long ruleId) {
		Rule dbRule = ruleDao.getRuleById(ruleId);
		if (dbRule == null) {
			throw new ServiceException(CodeConstant.MUS_101, messageUtil.getMsg(CodeConstant.MUS_101));
		}

		ruleDao.deleteById(ruleId);
		
		//递归删除权限子级的所有权限
		List<Rule> ruleList = new ArrayList<>();
		List<Rule> ruleTree = this.getRuleTree(ruleId);
		for (Rule rule : ruleTree) {
			ruleList.addAll(this.getAllByTree(rule));
		}
		for (Rule rule : ruleList) {
			ruleDao.deleteById(rule.getId());
		}

		// 删除权限对应的所有角色关系
		List<RoleRule> roleRuleList = roleRuleService.getRoleRuleListByRuleId(ruleId);
		for (Rule rule : ruleTree) {
			List<RoleRule> rrList = roleRuleService.getRoleRuleListByRuleId(rule.getId());
			roleRuleList.addAll(rrList);
		}
		for (RoleRule roleRule : roleRuleList) {
			roleRuleService.delete(roleRule.getId());
		}
		
		
		//打印日志
		try {
			String dbRuleJson = objectMapper.writeValueAsString(dbRule);
			String childsJson = objectMapper.writeValueAsString(ruleList);
			String roleRuleListJson = objectMapper.writeValueAsString(roleRuleList);
			log.warn("\n删除操作\n权限:{}，\n子级权限：{}，\n角色权限关联：{}",dbRuleJson,childsJson,roleRuleListJson);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * 递归展开权限里面所有的子集权限
	 * @param list
	 * @return
	 */
	private List<Rule> getAllByTree(Rule rule){
		List<Rule> list = new ArrayList<>();
		
		list.add(rule);
		
		if(rule.getChilds() != null && rule.getChilds().size() > 0) {
			for (Rule r : rule.getChilds()) {
				list.addAll(this.getAllByTree(r));
			}
		}
		
		return list;
	}
	
	@Cacheable(cacheNames = "ruleListByType", key = "#type")
	@Override
	public List<Rule> getRuleList(Integer type) {
		return ruleDao.getRuleList(type);
	}

	@Cacheable(cacheNames = "ruleList", key = "'ruleList'")
	@Override
	public List<Rule> getRuleList() {
		//TODO 改成page分页，增加type，name，code查询
		return ruleDao.getRuleList();
	}

	@Cacheable(cacheNames = "rule", key = "#ruleId")
	@Override
	public Rule getRule(Long ruleId) {
		return ruleDao.getRuleById(ruleId);
	}

	@Cacheable(cacheNames = "ruleListByParentId", key = "#parentId")
	@Override
	public List<Rule> getRuleListByParentId(Long parentId) {
		return ruleDao.getRuleListByParentId(parentId);
	}

	
	@Override
	public List<Rule> getRuleTree(Long parentId) {
		List<Rule> ruleList = self.getRuleListByParentId(parentId);
		for (Rule rule : ruleList) {
			List<Rule> list = this.getRuleTree(rule.getId());
			if (list != null && list.size() > 0) {
				rule.setChilds(list);
			}
		}

		return ruleList;
	}

}
